package com.smile.commons.gps;

import com.smile.commons.gps.domain.CoordinatePointReq;
import lombok.Data;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * 面状航线服务工具类
 * 整合各种航线生成和优化功能，提供统一的服务接口
 *
 * @author System
 * @since 2024-09-18
 */
@Data
public class PlanarAirlineService {

    /**
     * 面状航线生成结果
     */
    @Data
    public static class PlanarAirlineResult {
        /**
         * 航线点列表
         */
        private List<CoordinatePointReq> flightPath;

        /**
         * 航线总长度（米）
         */
        private double totalDistance;

        /**
         * 预估飞行时间（秒）
         */
        private double estimatedFlightTime;

        /**
         * 预估飞行时间（秒）
         */
        private double executeTime;

        /**
         * 预估照片数量
         */
        private int estimatedPhotoCount;

        /**
         * 预估照片数量
         */
        private int totalPhotos;


        /**
         * 覆盖面积（平方米）
         */
        private double coverageArea;

        // Getters and Setters
        public List<CoordinatePointReq> getFlightPath() {
            return flightPath;
        }

        public void setFlightPath(List<CoordinatePointReq> flightPath) {
            this.flightPath = flightPath;
        }

        public double getTotalDistance() {
            return totalDistance;
        }

        public void setTotalDistance(double totalDistance) {
            this.totalDistance = totalDistance;
        }

        public double getEstimatedFlightTime() {
            return estimatedFlightTime;
        }

        public void setEstimatedFlightTime(double estimatedFlightTime) {
            this.estimatedFlightTime = estimatedFlightTime;
        }

        public int getEstimatedPhotoCount() {
            return estimatedPhotoCount;
        }

        public void setEstimatedPhotoCount(int estimatedPhotoCount) {
            this.estimatedPhotoCount = estimatedPhotoCount;
        }

        public double getCoverageArea() {
            return coverageArea;
        }

        public void setCoverageArea(double coverageArea) {
            this.coverageArea = coverageArea;
        }
    }

    /**
     * 生成完整的面状航线
     *
     * @param boundaryPoints 边界点列表
     * @param config         航线配置
     * @param flightSpeed    飞行速度（米/秒），默认12m/s
     * @return 航线生成结果
     */
    public static PlanarAirlineResult generateCompletePlanarAirline(
            List<CoordinatePointReq> boundaryPoints,
            PlanarAirlineGenerator.AirlineConfig config,
            Double flightSpeed) {

        if (flightSpeed == null) {
            flightSpeed = 12.0; // 默认飞行速度12m/s
        }
        // 1. 生成基础航线
        List<CoordinatePointReq> flightPath = PlanarAirlineGenerator.generatePlanarAirline(boundaryPoints, config);

        // 2. 优化航线路径
        flightPath = AirlinePathOptimizer.optimizeFlightPath(flightPath, 0.00001); // 1米容差

        // 3. 添加平滑转弯点
        flightPath = AirlinePathOptimizer.addTurnPoints(flightPath, 20.0); // 20米转弯半径

        // 4. 计算航线统计信息
        PlanarAirlineResult result = new PlanarAirlineResult();
        result.setFlightPath(flightPath);
        result.setTotalDistance(AirlinePathOptimizer.calculateTotalDistance(flightPath));
        result.setEstimatedFlightTime(AirlinePathOptimizer.estimateFlightTime(flightPath, flightSpeed));

        // 计算拍照间距
        double gsd = calculateGSD(config.getFlightHeight().doubleValue(), config.getFocalLength(), config.getSensorWidth(), config.getImageWidth());
        double photoSpacing = calculatePhotoSpacing(gsd, config.getSensorWidth(), config.getImageWidth(), config.getOverlapH());
        result.setEstimatedPhotoCount(AirlinePathOptimizer.estimatePhotoCount(flightPath, photoSpacing));

        // 计算覆盖面积
        result.setCoverageArea(calculatePolygonArea(boundaryPoints));

        return result;
    }

    /**
     * 生成针对高德地图优化的面状航线
     * 解决靠近边框的航点在高德地图上显示误差问题
     *
     * @param boundaryPoints 边界点列表（高德GCJ02坐标系）
     * @param config         航线配置
     * @param flightSpeed    飞行速度（米/秒）
     * @return 高德优化的航线生成结果
     */
    public static PlanarAirlineResult generateGaodeOptimizedAirline(
            List<CoordinatePointReq> boundaryPoints,
            PlanarAirlineGenerator.AirlineConfig config,
            Double flightSpeed) {

        if (flightSpeed == null) {
            flightSpeed = 12.0;
        }

        // 1. 启用高德优化模式
        config.setGaodeOptimized(true);

        // 2. 设置更严格的边界限制，防止超出显示
        if (config.getMargin() == null || config.getMargin() > 10) {
            config.setMargin(5); // 设置小的边界裕量
        }

        // 3. 生成基础航线
        List<CoordinatePointReq> flightPath = PlanarAirlineGenerator.generatePlanarAirline(boundaryPoints, config);

        // 4. 进行高精度边界验证和调整
        flightPath = refineFlightPathForGaode(flightPath, boundaryPoints);

        // 5. 优化航线路径
        flightPath = AirlinePathOptimizer.optimizeFlightPath(flightPath, 0.000005); // 更高精度

        // 6. 添加平滑转弯点
        flightPath = AirlinePathOptimizer.addTurnPoints(flightPath, 15.0); // 更小转弯半径

        // 7. 计算统计信息
        PlanarAirlineResult result = new PlanarAirlineResult();
        result.setFlightPath(flightPath);
        result.setTotalDistance(AirlinePathOptimizer.calculateTotalDistance(flightPath));
        result.setEstimatedFlightTime(AirlinePathOptimizer.estimateFlightTime(flightPath, flightSpeed));

        double gsd = calculateGSD(config.getFlightHeight().doubleValue(), config.getFocalLength(), config.getSensorWidth(), config.getImageWidth());
        double photoSpacing = calculatePhotoSpacing(gsd, config.getSensorWidth(), config.getImageWidth(), config.getOverlapH());
        result.setEstimatedPhotoCount(AirlinePathOptimizer.estimatePhotoCount(flightPath, photoSpacing));
        result.setCoverageArea(calculatePolygonArea(boundaryPoints));

        return result;
    }

    /**
     * 针对高德地图精细调整航线路径
     */
    private static List<CoordinatePointReq> refineFlightPathForGaode(List<CoordinatePointReq> flightPath,
                                                                     List<CoordinatePointReq> boundaryPoints) {
        List<CoordinatePointReq> refinedPath = new ArrayList<>();

        for (CoordinatePointReq point : flightPath) {
            // 检查边界距离
            double minDistanceToBoundary = calculateMinDistanceToBoundary(point, boundaryPoints);

            // 如果太靠近边界（小于2米），进行微调
            if (minDistanceToBoundary < 0.00002) { // 约2米
                CoordinatePointReq adjustedPoint = adjustPointAwayFromBoundary(point, boundaryPoints);
                if (adjustedPoint != null) {
                    refinedPath.add(adjustedPoint);
                } else {
                    refinedPath.add(point); // 如果调整失败，保留原点
                }
            } else {
                refinedPath.add(point);
            }
        }

        return refinedPath;
    }

    /**
     * 计算点到边界的最短距离
     */
    private static double calculateMinDistanceToBoundary(CoordinatePointReq point, List<CoordinatePointReq> boundary) {
        double minDistance = Double.MAX_VALUE;

        for (int i = 0; i < boundary.size(); i++) {
            int next = (i + 1) % boundary.size();
            double distance = pointToLineSegmentDistance(
                    point.getLatitude().doubleValue(), point.getLongitude().doubleValue(),
                    boundary.get(i).getLatitude().doubleValue(), boundary.get(i).getLongitude().doubleValue(),
                    boundary.get(next).getLatitude().doubleValue(), boundary.get(next).getLongitude().doubleValue()
            );
            minDistance = Math.min(minDistance, distance);
        }

        return minDistance;
    }

    /**
     * 计算点到线段的距离
     */
    private static double pointToLineSegmentDistance(double px, double py, double x1, double y1, double x2, double y2) {
        double dx = x2 - x1;
        double dy = y2 - y1;

        if (dx == 0 && dy == 0) {
            return Math.sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
        }

        double t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy);
        t = Math.max(0, Math.min(1, t));

        double closestX = x1 + t * dx;
        double closestY = y1 + t * dy;

        return Math.sqrt((px - closestX) * (px - closestX) + (py - closestY) * (py - closestY));
    }

    /**
     * 将点调整到远离边界的位置
     */
    private static CoordinatePointReq adjustPointAwayFromBoundary(CoordinatePointReq point, List<CoordinatePointReq> boundary) {
        // 简单的调整策略：向多边形中心方向微调
        double centerLat = boundary.stream().mapToDouble(p -> p.getLatitude().doubleValue()).average().orElse(0);
        double centerLon = boundary.stream().mapToDouble(p -> p.getLongitude().doubleValue()).average().orElse(0);

        double currentLat = point.getLatitude().doubleValue();
        double currentLon = point.getLongitude().doubleValue();

        // 向中心方向移动小距离
        double adjustRatio = 0.1; // 移动10%的距离
        double newLat = currentLat + (centerLat - currentLat) * adjustRatio;
        double newLon = currentLon + (centerLon - currentLon) * adjustRatio;

        CoordinatePointReq adjustedPoint = new CoordinatePointReq();
        adjustedPoint.setLatitude(new BigDecimal(newLat).setScale(8, RoundingMode.HALF_UP));
        adjustedPoint.setLongitude(new BigDecimal(newLon).setScale(8, RoundingMode.HALF_UP));
        adjustedPoint.setHeight(point.getHeight());

        return adjustedPoint;
    }

    public static List<CoordinatePointReq> convertCoordinateSystem(List<CoordinatePointReq> flightPath, int sourceType) {
        if (sourceType == 0) {
            return flightPath; // 已经是WGS84，无需转换
        }

        for (CoordinatePointReq point : flightPath) {
            double lat = point.getLatitude().doubleValue();
            double lon = point.getLongitude().doubleValue();

            double[] converted;
            if (sourceType == 1) {
                // 百度坐标转WGS84
                converted = GeoConvertUtils.baiduToGps(lat, lon);
            } else if (sourceType == 2) {
                // 高德坐标转WGS84
                converted = GeoConvertUtils.gaodeToGps(lat, lon);
            } else {
                continue; // 未知类型跳过
            }

            point.setLatitude(new BigDecimal(converted[0]).setScale(8, RoundingMode.HALF_UP));
            point.setLongitude(new BigDecimal(converted[1]).setScale(8, RoundingMode.HALF_UP));
        }

        return flightPath;
    }

    /**
     * 将WGS84坐标转换为高德坐标（用于在高德地图上显示）
     */
    public static List<CoordinatePointReq> convertToGaodeCoordinate(List<CoordinatePointReq> flightPath) {
        List<CoordinatePointReq> gaodeFlightPath = new ArrayList<>();

        for (CoordinatePointReq point : flightPath) {
            double lat = point.getLatitude().doubleValue();
            double lon = point.getLongitude().doubleValue();

            // WGS84转高德GCJ02
            double[] gaodeCoord = GeoConvertUtils.gpsToGaode(lat, lon);

            CoordinatePointReq gaodePoint = new CoordinatePointReq();
            gaodePoint.setLatitude(new BigDecimal(gaodeCoord[0]).setScale(8, RoundingMode.HALF_UP));
            gaodePoint.setLongitude(new BigDecimal(gaodeCoord[1]).setScale(8, RoundingMode.HALF_UP));
            gaodePoint.setHeight(point.getHeight());

            gaodeFlightPath.add(gaodePoint);
        }

        return gaodeFlightPath;
    }

    /**
     * 计算地面采样距离（GSD）
     */
    private static double calculateGSD(double flightHeight, double focalLength, double sensorSize, int imageSize) {
        return (flightHeight * sensorSize) / (focalLength * imageSize);
    }

    /**
     * 计算拍照间距
     */
    private static double calculatePhotoSpacing(double gsd, double sensorWidth, int imageWidth, int overlapPercent) {
        double groundCoverage = gsd * imageWidth;
        double overlap = overlapPercent / 100.0;
        return groundCoverage * (1 - overlap);
    }

    /**
     * 计算多边形面积（使用shoelace公式）
     */
    private static double calculatePolygonArea(List<CoordinatePointReq> polygon) {
        if (polygon.size() < 3) {
            return 0;
        }

        double area = 0;
        int n = polygon.size();

        for (int i = 0; i < n; i++) {
            int j = (i + 1) % n;
            double lat1 = Math.toRadians(polygon.get(i).getLatitude().doubleValue());
            double lon1 = Math.toRadians(polygon.get(i).getLongitude().doubleValue());
            double lat2 = Math.toRadians(polygon.get(j).getLatitude().doubleValue());
            double lon2 = Math.toRadians(polygon.get(j).getLongitude().doubleValue());

            area += (lon2 - lon1) * (2 + Math.sin(lat1) + Math.sin(lat2));
        }

        area = Math.abs(area) * 6378137.0 * 6378137.0 / 2.0; // 地球半径平方
        return area;
    }

    /**
     * 验证航线参数
     *
     * @param boundaryPoints 边界点
     * @param config         配置参数
     * @return 验证结果
     */
    public static ValidationResult validateAirlineParameters(List<CoordinatePointReq> boundaryPoints, PlanarAirlineGenerator.AirlineConfig config) {
        ValidationResult result = new ValidationResult();
        result.setValid(true);

        // 检查边界点
        if (boundaryPoints == null || boundaryPoints.size() < 3) {
            result.setValid(false);
            result.addError("边界点数量至少需要3个");
        }

        // 检查高度
        if (config.getFlightHeight() == null || config.getFlightHeight().compareTo(BigDecimal.ZERO) <= 0) {
            result.setValid(false);
            result.addError("飞行高度必须大于0");
        }

        // 检查重叠率
        if (config.getOverlapH() == null || config.getOverlapH() < 30 || config.getOverlapH() > 90) {
            result.setValid(false);
            result.addError("航向重叠率应在30%-90%之间");
        }

        if (config.getOverlapW() == null || config.getOverlapW() < 30 || config.getOverlapW() > 90) {
            result.setValid(false);
            result.addError("旁向重叠率应在30%-90%之间");
        }

        // 检查相机参数
        if (config.getFocalLength() == null || config.getFocalLength() <= 0) {
            result.setValid(false);
            result.addError("相机焦距必须大于0");
        }

        return result;
    }

    /**
     * 验证结果类
     */
    public static class ValidationResult {
        private boolean valid;
        private List<String> errors = new ArrayList<>();

        public boolean isValid() {
            return valid;
        }

        public void setValid(boolean valid) {
            this.valid = valid;
        }

        public List<String> getErrors() {
            return errors;
        }

        public void addError(String error) {
            this.errors.add(error);
        }
    }

    /**
     * 计算航线信息摘要
     *
     * @param result 航线生成结果
     * @return 信息摘要字符串
     */
    public static String generateAirlineSummary(PlanarAirlineResult result) {
        StringBuilder summary = new StringBuilder();
        summary.append("航线信息摘要：\n");
        summary.append(String.format("航点数量：%d个\n", result.getFlightPath().size()));
        summary.append(String.format("航线总长：%.2f公里\n", result.getTotalDistance() / 1000.0));
        summary.append(String.format("预估飞行时间：%.1f分钟\n", result.getEstimatedFlightTime() / 60.0));
        summary.append(String.format("预估照片数量：%d张\n", result.getEstimatedPhotoCount()));
        summary.append(String.format("覆盖面积：%.2f平方公里\n", result.getCoverageArea() / 1000000.0));

        return summary.toString();
    }
}